/*
	Copyright (C) 2010 2011 Rémi Thebault

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <QPen>
#include <QColor>

#include "ganttscene.h"
#include "ganttmtask.h"
#include "ganttatask.h"
#include "ganttcycle.h"
#include "ganttlink.h"
#include "ganttaxis.h"
#include "procycle.h"
#include "taskviewhelper.h"

#include <procycle/cycle.h>



GanttScene::GanttScene(ProCycle* app, QRectF rect, QObject *parent) :
	QGraphicsScene(rect, parent),
	PCChild (app),
	m_cycle(app->activeCycle())
{
	m_axis = new GanttAxis(app);
	addItem(m_axis);
	m_axis->setPos(0, 0);

	m_ganttCycle = new GanttCycle(app, m_cycle);
	addItem(m_ganttCycle);
	m_ganttCycle->setPos(0, app->headerHeight());
	m_ganttCycle->replaceChildren();

	int ct = m_axis->unit2px(m_cycle->length());
	m_cycleTime = addLine(QLine(ct, 0, ct, height()), QPen(Qt::red));
	m_cycleTime->setZValue(1);
}



void GanttScene::resetCycle(PC::Cycle *cycle) {
	m_cycle = cycle;
	m_ganttCycle->resetTask(m_cycle);
	int ct = m_axis->unit2px(m_cycle->length());
	m_cycleTime->setLine(QLine(ct, 0, ct, height()));
	update();
}



GanttTask* GanttScene::createGTask(ProCycle *app, PC::Task *task, GanttCycle* gcycle, GanttMTask *parent) {
	if (PC::AtomTask* at = dynamic_cast<PC::AtomTask*>(task)) {
		return new GanttATask(app, at, gcycle, parent);
	}
	if (PC::MacroTask* mt = dynamic_cast<PC::MacroTask*>(task)) {
		return new GanttMTask(app, mt, gcycle, parent);
	}
	return 0;
}



GanttTask* GanttScene::resolvTask(QString tbsId) {
	QString cTbsId = m_cycle->tbsId();
	if (!tbsId.startsWith(cTbsId)) {
		return 0;
	}
	if (tbsId.length() == cTbsId.length()) {
		return m_ganttCycle;
	}
	tbsId = tbsId.right(tbsId.length()-cTbsId.length()-1);
	return m_ganttCycle->resolvTask(tbsId);
}


GanttTask* GanttScene::taskAt(const QPointF &p) {
	QGraphicsItem* i = itemAt(p);
	if (GanttTask* gt = dynamic_cast<GanttTask*>(i)) {
		return gt;
	}
	return 0;
}



void GanttScene::editModeChanged() {
	m_ganttCycle->editModeChanged();
	update();
}



void GanttScene::initSizePolicy(const QSize& size) {
	setSceneRect(0, 0, size.width(), size.height());
	m_preferredWidth = size.width();
	m_axis->setLength(60);
	int ct = m_axis->unit2px(m_cycle->length());
	m_cycleTime->setLine(QLine(ct, 0, ct, height()));
	update();
}


void
GanttScene::processViewResize(QSize newSize) {
	if (newSize.width() > m_preferredWidth) {
		setSceneRect(0, 0, newSize.width(), newSize.height());
	}
	else {
		setSceneRect(0, 0, m_preferredWidth, newSize.height());
	}
	m_axis->setScale(m_axis->scale());
	int ct = m_axis->unit2px(m_cycle->length());
	m_cycleTime->setLine(QLine(ct, 0, ct, height()));
	update();
}


void GanttScene::processExpansionChange(PC::Task* task) {
	m_ganttCycle->replaceChildren();
	m_ganttCycle->processExpansionChange(task);
	this->update();
}



void GanttScene::processTaskTimeChanged(PC::Task* task) {
	GanttTask* gt = resolvTask(task->tbsId());
	if (gt) {
		gt->update(gt->boundingRect());
		int ct = m_axis->unit2px(m_cycle->length());
		m_cycleTime->setLine(QLine(ct, 0, ct, height()));
	}
}


void GanttScene::processTaskValueChanged(PC::Task* task, PC::Task::Data /*data*/) {
	GanttTask* gt = resolvTask(task->tbsId());
	gt->update(gt->boundingRect());
}


void GanttScene::processTaskCreated(PC::MacroTask *parent, PC::Task *task) {
	GanttMTask* gp = static_cast<GanttMTask*>(resolvTask(parent->tbsId()));
	GanttTask* gt = createGTask(app(), task, m_ganttCycle, gp);
	gp->insertTask(gt, task->pos());
	m_ganttCycle->replaceChildren();
}


void GanttScene::processTaskWillDelete(PC::Task* task) {
	GanttTask* gt = resolvTask(task->tbsId());
	GanttMTask* gp = static_cast<GanttMTask*>(resolvTask(task->parent()->tbsId()));
	gp->removeTask(gt);
	removeItem(gt);
	delete gt;
	m_ganttCycle->replaceChildren();
}


void GanttScene::processTaskTransformed(PC::MacroTask *parent, int taskPos) {
	GanttMTask* gmp = static_cast<GanttMTask*>(resolvTask(parent->tbsId()));
	GanttTask* gt = resolvTask(PC::Task::makeTbsId(parent, taskPos));
	PC::Task* task = parent->task(taskPos);
	gmp->removeTask(gt);
	removeItem(gt);
	delete gt;

	gt = createGTask(app(), task, m_ganttCycle, gmp);
	gmp->insertTask(gt, taskPos);

	m_ganttCycle->replaceChildren();
}

void GanttScene::processTaskMoved(PC::MacroTask *parentTo, PC::Task *parentFrom, int posTo, int posFrom) {
	GanttMTask* gpFrom = static_cast<GanttMTask*>(resolvTask(parentFrom->tbsId()));
	GanttMTask* gpTo = static_cast<GanttMTask*>(resolvTask(parentTo->tbsId()));

	GanttTask* gt = resolvTask(PC::Task::makeTbsId(parentFrom, posFrom));

	gpFrom->removeTask(gt);
	gpTo->addTask(gt);

	m_ganttCycle->replaceChildren();
}

void GanttScene::processLinkCreated(PC::Link *link) {
	GanttLink* gl = new GanttLink(app(), m_ganttCycle, link);
	m_ganttCycle->addLink(gl);
	gl->setPos(0, 0);
	update();
}

void GanttScene::processLinkWillDelete(PC::Link *link) {
	GanttLink* gl = m_ganttCycle->findGLink(link);
	if (gl) {
		m_ganttCycle->removeLink(gl);
		removeItem(gl);
		delete gl;
	}
}


void GanttScene::processScheduled() {
	m_ganttCycle->processScheduled();
}



void GanttScene::selectTask(PC::Task *selected, PC::Task* unselected) {
	GanttTask* gt;
	if (unselected) {
		if (gt = resolvTask(unselected->tbsId())) {
			gt->setSelected(false);
		}
	}
	if (selected) {
		if (gt = resolvTask(selected->tbsId())) {
			gt->setSelected(true);
		}
	}
}



void GanttScene::selectLink(PC::Link *selected, PC::Link* unselected) {
	GanttLink* gl;
	if (unselected) {
		if(gl = m_ganttCycle->findGLink(unselected)) {
			gl->setSelected(false);
		}
	}
	if (selected) {
		if(gl = m_ganttCycle->findGLink(selected)) {
			gl->setSelected(true);
		}
	}
}



void GanttScene::toggleCriticalPath(bool on) {
	m_ganttCycle->setCycleProp(PC::Cycle::PROP_DRAWCRITICAL, QVariant(on));
	update();
}


void GanttScene::toggleCycleOverlap(bool on) {
	m_ganttCycle->setCycleProp(PC::Cycle::PROP_DRAWOVERLAP, QVariant(on));
	update();
}


void GanttScene::toggleTasksFloat(bool on) {
	m_ganttCycle->setCycleProp(PC::Cycle::PROP_DRAWFLOATS, QVariant(on));
	update();
}



void GanttScene::zoomFit() {

}


const float zoomUnit = 1.2f;

void GanttScene::zoomIn() {
	m_axis->setScale(m_axis->scale() * zoomUnit);
	update();
}

void GanttScene::zoomOut() {
	m_axis->setScale(m_axis->scale() / zoomUnit);
	update();
}

void GanttScene::toggleZoomWindow(bool on) {

}



QColor GanttScene::colorForTask(PC::Task *task) {
	return QColor(colorStrForTask(task));
}


QString GanttScene::colorStrForTask(PC::Task *task) {
	QVariant v = task->property("color");
	if (v.isValid()) {
		return v.toString();
	}
	if (dynamic_cast<PC::AtomTask*>(task)) {
		return DEFAULT_ATASK_COLOR;
	}
	return DEFAULT_MTASK_COLOR;
}

// returns the same color with max saturation
QColor GanttScene::borderColor(const QColor &back) {
	return QColor::fromHsv(back.hue(), 255, back.value());
}
